package net.ashrentum;

import java.util.ArrayList;

public class MultipleTimeFrame implements Timer {

	ArrayList<TimeFrame> timeFrames;
	
	long time;
	
	MultipleTimeFrame(TimeFrame ... fs) {
		if (fs != null)
			timeFrames = new ArrayList<TimeFrame>();
		time = 0;
		for (TimeFrame f : fs)
			this.combineTimeFrame(f);
	}
	
	private static long getCombinedTime(TimeFrame f1, TimeFrame f2) {
		long start1 = f1.start;
		long end1 	= f1.end;
		long start2 = f2.start;
		long end2 	= f2.end;
		boolean first1;
		boolean last1;
		
		if (start1 == Math.min(start1, start2))
			first1 = true;
		else
			first1 = false;
		
		if (end1 == Math.max(end1, end2))
			last1 = true;
		else
			last1 = false;
		
		/* One inside the other */
		if (first1 && last1) 
			return f1.getTime();
		
		/* Overlapping */
		if (end1 > start2)
			return end2 - start1;
		
		/* No overlapping */
		return f1.getTime() + f2.getTime();
	}
	
	MultipleTimeFrame combineTimeFrame(TimeFrame f2) {		
		int i;
		
		for (i = 0; i < timeFrames.size(); i++) {
			TimeFrame f = timeFrames.get(i);
			
			/* Before. Insert right away */
			if (f2.end < f.start) {
				timeFrames.add(i, f2);
				time += f2.getTime();
				break;
			}
			/* After. See how it relates with the next frame */
			if (f.end < f2.start) {
				continue;
			}
			/* They overlap */
			time -= f.getTime();
			long combined = getCombinedTime(f, f2);
			boolean expansion = f2.end > f.end;
			f2.start = Math.min(f.start, f2.start);
			f2.end   = Math.max(f.end,   f2.end);
			if (expansion) {
				timeFrames.remove(i);
				return this.combineTimeFrame(f2);
			}
			else {
				timeFrames.set(i, f2);
				time += combined;
				break;
			}			
		}
		
		/* At the end. All continued */
		if (timeFrames.size() == i) {
			timeFrames.add(f2);
			time += f2.getTime();
		}
		
		return this;
	}

	MultipleTimeFrame combineTimeFrame(MultipleTimeFrame mf2) {
		for (TimeFrame f2 : mf2.timeFrames)
			this.combineTimeFrame(f2);
		return this;
	}
	
	@Override
	public String toString() {
		String ret = "";
		final String sep = "  ...  ";
		for (TimeFrame f : timeFrames) {
			ret += f + sep;
		}
		return ret.substring(0, ret.length() - sep.length());
	}
	
	/*
	 * Improve this and constructor to speed it up a little bit.
	 * 
	 */
	@Override
	public void start() {
		timeFrames = new ArrayList<TimeFrame>(1);
		timeFrames.add(new TimeFrame(0, 0));
		timeFrames.get(0).start();
	}

	@Override
	public void stop() {
		timeFrames.get(0).stop();
		time = timeFrames.get(0).end - timeFrames.get(0).start;
	}

	@Override
	public final long getTime() {
		return time;
	}
	
	public static void main(String[] args) {
		MultipleTimeFrame mf = new MultipleTimeFrame(
				new TimeFrame(0, 3), new TimeFrame(8, 9), new TimeFrame(1, 4), new TimeFrame(-8, -2), new TimeFrame(82, 83));
		System.out.println(mf);
		mf.combineTimeFrame(new TimeFrame(-100, 100));
		System.out.println(mf);
		mf.combineTimeFrame(new TimeFrame(-100, 100));
		System.out.println(mf);
		mf.combineTimeFrame(new TimeFrame(99, 100));
		System.out.println(mf);
		mf.combineTimeFrame(new TimeFrame(99, 101));
		System.out.println(mf);
		mf.combineTimeFrame(new TimeFrame(101, 101));
		System.out.println(mf);
		mf.combineTimeFrame(new TimeFrame(101, 108));
		System.out.println(mf);
		mf.combineTimeFrame(new TimeFrame(110, 114));
		System.out.println(mf);
		mf.combineTimeFrame(new TimeFrame(128, 130));
		System.out.println(mf);
		mf.combineTimeFrame(new TimeFrame(100, 115));
		System.out.println(mf.getTime() + " " + mf);
		
		
		mf = new MultipleTimeFrame();
		mf.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		mf.stop();
		System.out.println(mf.getTime() + " " + mf);
	}
}
